#pragma once

#include "znx_event.h"
#include "znx_worker_proc.h"
#include "comm/znx_log.h"
#include "comm/znx_rbtree.h"

// you can set the key to dispatch_event->save_data_1.
// we (sf) set do_handler to dispatch_event->save_data_2.
// we (sf) set result to dispatch_event->save_data_3.

typedef int (*znx_sf_node_cmp_pt) (znx_dispatch_event_t *a,
	znx_dispatch_event_t *b);

typedef znx_dispatch_event_t *
	(*znx_sf_create_master_pt)(znx_dispatch_event_t *old);

typedef void (*znx_sf_hold_result_pt)(void *result, znx_bool_t is_master);

typedef struct znx_single_flight_s znx_single_flight_t;

typedef struct {
	znx_rbtree_node_t 		tree_node;
	znx_single_flight_t		*owner_sf;
	znx_dispatch_event_t 	*master;
	znx_queue_t 			pending;
} znx_single_flight_call_t;


struct znx_single_flight_s {
	znx_spinlock_t 			lock;
	znx_rbtree_t 			tree;
	znx_rbtree_node_t 		tree_sentinel;
	znx_sf_node_cmp_pt		cmp;
	znx_sf_create_master_pt create_master;
	znx_sf_hold_result_pt	hold_result;
};


static inline int
znx_single_flight_call_cmp(znx_rbtree_node_t *node_a,
	znx_rbtree_node_t *node_b)
{
	znx_single_flight_call_t *a = (znx_single_flight_call_t *)node_a;
	znx_single_flight_call_t *b = (znx_single_flight_call_t *)node_b; 

	if (a->owner_sf == NULL || a->owner_sf != b->owner_sf) {
		ZNX_DEF_LOG_FATAL("unexpect state");
	}

	znx_single_flight_t	*sf = a->owner_sf;
	return sf->cmp(a->master, b->master);
}

/**
 * if create_master handler is not NULL
 * when creating a znx_single_flight_call_t for the first time,
 * we will call create_master to create a master dispatch_event task
 * to replace the event passed in by
 * znx_single_flight_do for the first time.
 *
 * the callback of the task created by create master,
 * usually only notify and release itself.
 */

static inline void
znx_single_flight_init(znx_single_flight_t *sf,
	znx_sf_node_cmp_pt cmp,
	znx_sf_create_master_pt create_master,
	znx_sf_hold_result_pt hold_result)
{
	if (cmp == NULL) {
		ZNX_DEF_LOG_FATAL("expect a non-null cmp handle");
	}

	sf->lock = ZNX_SPINLOCK_INITLIZER;
	sf->cmp = cmp;
	sf->create_master = create_master;
	sf->hold_result = hold_result;
	znx_rbtree_init(&sf->tree, &sf->tree_sentinel,
		znx_single_flight_call_cmp);
}


void znx_single_flight_do(znx_single_flight_t *sf,
    znx_dispatch_event_t *dispatch_event,
    znx_dispatch_handler_t do_handler);


// we set result to dispatch_event->save_data_2.
void znx_single_flight_notify(znx_single_flight_t *sf,
    znx_dispatch_event_t *dispatch_event,
    void *result);


/**
 * when the function return ZNX_FALSE,
 * it mean that the task cancel failed.
 * the scene is as follows:
 * 1. the canceled task is the master.
 * 2. the canceled task is invalid task.
 * 3. the task has been processed, waiting for notification.
 *
 * when the task is successfully canceled, it will no longer
 * receive any notifications from single_flight.
 * otherwise single_flight will notify the task processing result.
 *
 * we do not provide a master task cancellation mechanism,
 * you can specify znx_single_flight_create_fake_task in znx_single_flight_init
 * avoid the existence of master task.
 */

znx_bool_t znx_single_flight_cancel(znx_single_flight_t *sf,
	znx_dispatch_event_t *dispatch_event);

